package com.tvserials.notification.utils;

import com.tvserials.notification.dto.Episode;
import com.tvserials.notification.dto.Serial;
import com.tvserials.notification.vo.SourceType;
import com.tvserials.notification.dto.EpisodeMedia;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.*;

/**
 * Created by oleksandr.dokienko on 3/25/2014.
 */
public class VOUtils {

    public static List<Episode> getLatestChapters(Date lastSyncTime, List<Episode> tvChapterVOs) {

        List<Episode> filteredList = new ArrayList<Episode>();
        for (Episode item : tvChapterVOs) {
            Date releaseDate = item.getReleaseDate();
            if (releaseDate.after(lastSyncTime)) {
                filteredList.add(item);
            }
        }
        return filteredList;
    }

    public static int getUniqueID(Episode tvChapterVO) {
        String chapterNumber = tvChapterVO.getChapterNumber();

        if (chapterNumber != null && chapterNumber.contains(".")) {
            chapterNumber = chapterNumber.replace(".", "").replace("-", "");
            return Integer.parseInt(tvChapterVO.getChapterName().length() + chapterNumber);
        }
        return new Random().nextInt();
    }

    public static List<Episode> strictList(List<Episode> episodeVOs, int i) {
        if (episodeVOs != null && !episodeVOs.isEmpty() && episodeVOs.size() > i) {
            Map<SourceType, List<Episode>> sourceTypeListMap = groupBySourceType(episodeVOs);
            ArrayList<Episode> episodes = new ArrayList<Episode>();
            for (SourceType sourceType : sourceTypeListMap.keySet()) {
                List<Episode> episodeSource = sourceTypeListMap.get(sourceType);
                if (i < episodeSource.size()) {
                    episodes.addAll(episodeSource.subList(0, i));
                } else {
                    episodes.addAll(episodeSource);
                }
            }
            return episodes;
        }
        return episodeVOs;
    }

    public static List<Episode> filterBySerials(List<Episode> episodesFromCache, List<Serial> serials) {
        List<Episode> episodes = new ArrayList<Episode>();
        if (episodesFromCache == null) {
            return episodes;
        }
        for (Episode episodeVO : episodesFromCache) {
            Serial tvSerialDTO = new Serial(episodeVO.getShowName(), episodeVO.getSourceType());
            if (serials.contains(tvSerialDTO)) {
                episodes.add(episodeVO);
            }
        }
        return episodes;
    }

    public static <T extends Collection> boolean isListEmpty(T list) {
        return list == null || list.isEmpty();
    }

    public static <T extends Collection> boolean isListNotEmpty(T list) {
        return list != null && !list.isEmpty();
    }

    /**
     * Main assumptions:
     * there are 3 video qualities, ids goes one by one
     *
     * @param episode
     * @param allRssItems
     * @return
     */
    public static List<EpisodeMedia> getRelatedTypes(Episode episode, List<EpisodeMedia> allRssItems) {
        List<EpisodeMedia> episodeMedias = new ArrayList<EpisodeMedia>();
        String siteId = episode.getSiteId();
        String chapterName = episode.getChapterName();

        for (EpisodeMedia episodesRssVO : allRssItems) {
            String link = episodesRssVO.getLink();
            link = link.substring(link.indexOf("=") + 1, link.indexOf("&"));
            String title = episodesRssVO.getTitle();

            boolean matchIds = siteId.equals(link);
            matchIds |= (String.valueOf(Integer.valueOf(siteId) + 1).equals(link));
            matchIds |= (String.valueOf(Integer.valueOf(siteId) + 2).equals(link));

            if (title.contains(chapterName) && (matchIds)) {
                episodeMedias.add(episodesRssVO);
            }

            if (episodeMedias.size() == 3) {
                //finish cycle to optimize no. of iterations
                return episodeMedias;
            }
        }
        return episodeMedias;
    }

    public static List<Episode> sortByDate(List<Episode> episodes) {
        if (isListNotEmpty(episodes)) {
            Collections.sort(episodes, new DateComparator());
        }
        return episodes;
    }

    public static List<Episode> groupItems(List<EpisodeMedia> episodeMedias) {
        List<Episode> episodes = new ArrayList<Episode>();
        Map<String, List<EpisodeMedia>> maps = new HashMap<String, List<EpisodeMedia>>();
        for (EpisodeMedia mediaVO : episodeMedias) {
            String title = mediaVO.getTitle();
            String serialName = title.substring(0, title.indexOf("/")).trim();
            if (maps.containsKey(serialName)) {
                maps.get(serialName).add(mediaVO);
            } else {
                List<EpisodeMedia> mediaVOs = new ArrayList<EpisodeMedia>();
                mediaVOs.add(mediaVO);
                maps.put(serialName, mediaVOs);
            }
        }
        for (List<EpisodeMedia> mediaVOList : maps.values()) {
            if (VOUtils.isListNotEmpty(mediaVOList)) {
                EpisodeMedia mediaVO = mediaVOList.get(0);
                String title = mediaVO.getTitle();
                String chapterNo = title.substring(title.indexOf("("), title.indexOf(")") + 1);

                Episode episodeVO = new Episode();
                episodeVO.setImage(loadImage(mediaVO.getImageLink()));
                episodeVO.setReleaseDate(mediaVO.getPubDate());
                episodeVO.setShowName(new Serial(title.substring(0, title.indexOf("(")).trim(), SourceType.NEW_STUDIO));
                episodeVO.setChapterName(chapterNo);
                episodeVO.setMediaQualities(mediaVOList);
                episodes.add(episodeVO);
            }
        }
        return VOUtils.sortByDate(episodes);
    }

    public static byte[] loadImage(String imageLink) {
        try {
            //Open a URL Stream
            URL url = new URL(imageLink);
            InputStream in = url.openStream();

            ByteArrayOutputStream out = new ByteArrayOutputStream();

            for (int b; (b = in.read()) != -1; ) {
                out.write(b);
            }

            out.close();

            in.close();

            return out.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static List<Episode> filterBySourceTypes(List<Episode> episodesFromCache, List<SourceType> sourceTypes) {
        List<Episode> episodes = new ArrayList<Episode>();
        for (Episode episodeVO : episodesFromCache) {
            if (sourceTypes.contains(episodeVO.getSourceType())) {
                episodes.add(episodeVO);
            }
        }
        return episodes;
    }

    public static List<Serial> getSelectedItems(List<Serial> data) {
        List<Serial> selectedItems = new ArrayList<Serial>();
        for (Serial item : data) {
            if (item.isSelected()) {
                selectedItems.add(item);
            }
        }
        return selectedItems;
    }

    public static Set<String> convertToSet(List<Serial> serialVOs) {
        HashSet<String> hashSet = new HashSet<String>();
        for (Serial serialVO : serialVOs) {
            hashSet.add(serialVO.getShowName());
        }
        return hashSet;
    }

    public static Map<Serial, List<Episode>> groupBySerials(List<Episode> episodes) {
        HashMap<Serial, List<Episode>> hashMap = new HashMap<Serial, List<Episode>>();
        for (Episode episodeVO : episodes) {
            String showName = episodeVO.getShowName();
            SourceType sourceType = episodeVO.getSourceType();
            Serial serial = new Serial(showName, sourceType);

            if (hashMap.containsKey(serial)) {
                hashMap.get(serial).add(episodeVO);
            } else {
                List<Episode> list = new ArrayList<Episode>(Arrays.asList(episodeVO));
                hashMap.put(new Serial(showName, sourceType), list);
            }
        }
        return hashMap;
    }

    public static Map<SourceType, List<Episode>> groupBySourceType(List<Episode> tvChapterVOs) {
        HashMap<SourceType, List<Episode>> hashMap = new HashMap<SourceType, List<Episode>>();
        for (Episode episodeVO : tvChapterVOs) {
            SourceType sourceType = episodeVO.getSourceType();

            if (hashMap.containsKey(sourceType)) {
                hashMap.get(sourceType).add(episodeVO);
            } else {
                List<Episode> list = new ArrayList<Episode>(Arrays.asList(episodeVO));
                hashMap.put(episodeVO.getSourceType(), list);
            }
        }
        return hashMap;
    }

    public static Date getLatestDate(List<Episode> cachedEpisodes, List<SourceType> sourceTypes) {
        if (sourceTypes.size() > 1) {
            //if Source system All, then we need to return latest episode date,
            // no matter from what source system it was
            return cachedEpisodes.get(0).getReleaseDate();
        }
        for (Episode episodeVO : cachedEpisodes) {
            if (sourceTypes.contains(episodeVO.getSourceType())) {
                return episodeVO.getReleaseDate();
            }
        }
        return null;
    }

    public static boolean isSignedOnSourceSystem(List<Serial> serials, SourceType sourceType) {
        for (Serial serialVO : serials) {
            if (sourceType.equals(serialVO.getSourceType())) {
                return true;
            }
        }
        return false;
    }

    public static List<Serial> getSerialsBySourceType(List<Serial> serials, SourceType sourceType) {
        ArrayList<Serial> list = new ArrayList<Serial>();
        for (Serial serial : serials) {
            if (serial.getSourceType().equals(sourceType)) {
                list.add(serial);
            }
        }
        return list;
    }

    public static Map<String, List<Serial>> groupSerialsAlphabetically(List<Serial> serials) {
        Map<String, List<Serial>> map = new HashMap<String, List<Serial>>();
        if (serials == null || serials.isEmpty()) {
            return map;
        }
        List<String> availableCategories = getSerialsCategories();
        map.put("#", new ArrayList<Serial>());
        for (Serial serial : serials) {
            String showName = serial.getShowName();
            if (showName != null && !showName.isEmpty()) {
                String firstLetter = String.valueOf(showName.charAt(0)).toUpperCase();
                boolean isValidKey = availableCategories.contains(firstLetter);
                if (isValidKey) {
                    if (map.containsKey(firstLetter)) {
                        map.get(firstLetter).add(serial);
                    } else {
                        ArrayList<Serial> value = new ArrayList<Serial>();
                        value.add(serial);
                        map.put(firstLetter, value);
                    }
                } else {
                    map.get("#").add(serial);
                }
            }
        }
        return map;
    }

    public static List<String> getSerialsCategories() {
        return Arrays.asList("А", "Б", "В", "Г", "Д", "Е", "Ж", "З", "Й", "И", "К", "Л", "М", "Н",
                "О", "П", "Р", "С", "Т", "У", "Ф", "Х", "Ц", "Ч", "Ш", "Щ", "Э", "Ю", "Я");
    }

    public static List<Serial> getSelectedItems(Map<String, List<Serial>> listDataChild) {
        ArrayList<Serial> serials = new ArrayList<Serial>();
        for (String key : listDataChild.keySet()) {
            List<Serial> selectedItems = getSelectedItems(listDataChild.get(key));
            if (!selectedItems.isEmpty()) {
                serials.addAll(selectedItems);
            }
        }
        return serials;
    }

    private static class DateComparator implements Comparator<Episode> {

        @Override
        public int compare(Episode lhs, Episode rhs) {
            if (rhs.getReleaseDate() != null) {
                return rhs.getReleaseDate().compareTo(lhs.getReleaseDate());
            }
            return -1;
        }
    }
}
